package cc.mrbird.febs.common.utils;

import cc.mrbird.febs.common.authentication.JWTUtil;
import cc.mrbird.febs.common.domain.FebsConstant;
import cc.mrbird.febs.common.function.CacheSelector;
import cc.mrbird.febs.common.service.CacheService;
import cc.mrbird.febs.system.domain.User;
import cc.mrbird.febs.system.service.UserService;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;

import java.util.function.Supplier;
import java.util.stream.IntStream;

/**
 * FEBS通用工具类
 * 提供缓存查询、用户信息获取、Token加密解密等功能
 * 包含驼峰命名转下划线等通用字符串处理方法
 * 
 * @author MrBird
 */
@Slf4j
public class FebsUtil {

    /**
     * 缓存查询模板方法
     * 先查询Redis缓存，如果缓存中没有数据则查询数据库
     * 如果缓存查询出错，则直接查询数据库
     *
     * @param cacheSelector 缓存查询方法，用于从Redis中获取数据
     * @param databaseSelector 数据库查询方法，用于从数据库中获取数据
     * @param <T> 返回数据的类型
     * @return 查询结果，优先返回缓存数据
     */
    public static <T> T selectCacheByTemplate(CacheSelector<T> cacheSelector, Supplier<T> databaseSelector) {
        try {
            log.debug("query data from redis ······");
            // 先查 Redis缓存
            T t = cacheSelector.select();
            if (t == null) {
                // 没有记录再查询数据库
                return databaseSelector.get();
            } else {
                return t;
            }
        } catch (Exception e) {
            // 缓存查询出错，则去数据库查询
            log.error("redis error：", e);
            log.debug("query data from database ······");
            return databaseSelector.get();
        }
    }

    /**
     * 获取当前登录用户信息
     * 从SecurityContext中获取token，解析出用户名
     * 优先从缓存中获取用户信息，缓存中没有则从数据库查询
     *
     * @return 当前登录用户对象
     */
    public static User getCurrentUser() {
        // 从SecurityContext中获取token
        String token = (String) SecurityUtils.getSubject().getPrincipal();
        // 从token中解析出用户名
        String username = JWTUtil.getUsername(token);
        // 获取用户服务和缓存服务
        UserService userService = SpringContextUtil.getBean(UserService.class);
        CacheService cacheService = SpringContextUtil.getBean(CacheService.class);

        // 优先从缓存中获取用户信息
        return selectCacheByTemplate(() -> cacheService.getUser(username), () -> userService.findByName(username));
    }

    /**
     * Token加密
     * 使用系统配置的密钥对token进行DES加密
     *
     * @param token 待加密的token
     * @return 加密后的token字符串，加密失败返回null
     */
    public static String encryptToken(String token) {
        try {
            EncryptUtil encryptUtil = new EncryptUtil(FebsConstant.TOKEN_CACHE_PREFIX);
            return encryptUtil.encrypt(token);
        } catch (Exception e) {
            log.info("token加密失败：", e);
            return null;
        }
    }

    /**
     * Token解密
     * 使用系统配置的密钥对加密的token进行DES解密
     *
     * @param encryptToken 加密后的token
     * @return 解密后的token字符串，解密失败返回null
     */
    public static String decryptToken(String encryptToken) {
        try {
            EncryptUtil encryptUtil = new EncryptUtil(FebsConstant.TOKEN_CACHE_PREFIX);
            return encryptUtil.decrypt(encryptToken);
        } catch (Exception e) {
            log.info("token解密失败：", e);
            return null;
        }
    }

    /**
     * 驼峰命名转下划线
     * 将驼峰命名的字符串转换为下划线形式
     * 例如：userName -> user_name
     *
     * @param value 待转换的驼峰命名字符串
     * @return 转换后的下划线形式字符串，如果输入为空则返回原值
     */
    public static String camelToUnderscore(String value) {
        if (StringUtils.isBlank(value))
            return value;
        // 按驼峰分割字符串
        String[] arr = StringUtils.splitByCharacterTypeCamelCase(value);
        if (arr.length == 0)
            return value;
        // 拼接下划线
        StringBuilder result = new StringBuilder();
        IntStream.range(0, arr.length).forEach(i -> {
            if (i != arr.length - 1)
                result.append(arr[i]).append(StringPool.UNDERSCORE);
            else
                result.append(arr[i]);
        });
        // 转换为小写
        return StringUtils.lowerCase(result.toString());
    }

}
